//name: Xingkai Zhou
//SID:1318620

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.net.UnknownHostException;


public class Client extends JFrame {
    private ClientSocket clientSocket = new ClientSocket();
    private JTextArea showArea;

    Client() {
        setTitle("Dictionary Client");
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setBounds(10, 10, 960, 260);
        init();
        setVisible(true);
    }

    void init() {
        JPanel northPanel = new JPanel();
        JTextField ip = new JTextField(10);
        JTextField port = new JTextField(4);
        northPanel.add(new JLabel("ip："));
        northPanel.add(ip);
        northPanel.add(new JLabel("port："));
        northPanel.add(port);
        JButton connButton = new JButton("connect");
        //conntect Button action listener
        connButton.addActionListener(e -> {
            try {
                String ipText = ip == null ? "" : ip.getText();
                String portText = port == null ? "" : port.getText();
                if (ipText == null || ipText.equals("")) {
                    throw new IllegalStateException("IP address cannot be empty");
                }
                if (portText == null || portText.equals("")) {
                    throw new IllegalStateException("Port cannot be empty");
                }
                clientSocket.connect(ipText, Integer.valueOf(portText));
            } catch (Exception e1) {
                JOptionPane.showMessageDialog(this, e1, "Error:", JOptionPane.ERROR_MESSAGE);
                return;
            }
            JOptionPane.showMessageDialog(this, "Connection established successfully!", "Hint", JOptionPane.INFORMATION_MESSAGE);
        });
        JTextField wordText = new JTextField(6);
        JTextField explanationText = new JTextField(6);
        JButton selectButton = new JButton("Search");
        selectButton.addActionListener(new QueryActionListener(wordText));
        JButton deleteButton = new JButton("Delete");
        deleteButton.addActionListener(new DeleteActionListener(wordText));
        JButton updateButton = new JButton("Update");
        updateButton.addActionListener(new UpdateActionListener(wordText, explanationText));
        JButton saveButton = new JButton("Add");
        saveButton.addActionListener(new SaveActionListener(wordText, explanationText));
        northPanel.add(connButton);
        northPanel.add(new JLabel("word："));
        northPanel.add(wordText);
        northPanel.add(new JLabel("meaning："));
        northPanel.add(explanationText);
        northPanel.add(selectButton);
        northPanel.add(deleteButton);
        northPanel.add(updateButton);
        northPanel.add(saveButton);
        add(northPanel, BorderLayout.NORTH);
        showArea = new JTextArea();
        add(new JScrollPane(showArea), BorderLayout.CENTER);
    }

    class QueryActionListener implements ActionListener {
        private JTextField textField;

        QueryActionListener(JTextField textField) {
            this.textField = textField;
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            try {
                String word = textField == null ? "" : textField.getText();
                if (word == null || word.equals("")) {
                    throw new IllegalStateException("Error: word is empty");
                }
                String s = clientSocket.query(new QueryRequest(QueryRequest.TYPE.SELECT, word, ""));
                showArea.setText("");
                showArea.append(s);
                showArea.append("\n");
            } catch (IllegalStateException |
                    IOException e1) {
                JOptionPane.showMessageDialog(getRootPane(), e1, "Error:", JOptionPane.ERROR_MESSAGE);
            }
        }

    }

    class UpdateActionListener implements ActionListener {
        private JTextField textField;
        private JTextField explainField;

        UpdateActionListener(JTextField textField, JTextField explainField) {
            this.textField = textField;
            this.explainField = explainField;
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            try {
                String word = textField == null ? "" : textField.getText();
                String explainFieldText = explainField == null ? "" : explainField.getText();
                if (word == null || word.equals("")) {
                    throw new IllegalStateException("Error: word is empty");
                }
                if (explainFieldText == null || explainFieldText.equals("")) {
                    throw new IllegalStateException("Error: meaning is empty");
                }

                String s = clientSocket.query(new QueryRequest(QueryRequest.TYPE.UPDATE, word, explainFieldText));
                showArea.setText("");
                showArea.append(s);
                showArea.append("\n");
            } catch (IllegalStateException |
                    IOException e1) {
                JOptionPane.showMessageDialog(getRootPane(), e1, "Error", JOptionPane.ERROR_MESSAGE);
            }
        }

    }

    class DeleteActionListener implements ActionListener {
        private JTextField textField;

        DeleteActionListener(JTextField textField) {
            this.textField = textField;
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            try {
                String word = textField == null ? "" : textField.getText();
                if (word == null || word.equals("")) {
                    throw new IllegalStateException("Error: word is empty");
                }
                String s = clientSocket.query(new QueryRequest(QueryRequest.TYPE.DELETE, word, ""));
                showArea.setText("");
                showArea.append(s);
                showArea.append("\n");
            } catch (IllegalStateException |
                    IOException e1) {
                JOptionPane.showMessageDialog(getRootPane(), e1, "Error", JOptionPane.ERROR_MESSAGE);
            }
        }

    }

    class SaveActionListener implements ActionListener {
        private JTextField textField;
        private JTextField explainField;

        SaveActionListener(JTextField textField, JTextField explainField) {
            this.textField = textField;
            this.explainField = explainField;
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            try {
                String word = textField == null ? "" : textField.getText();
                String explainFieldText = explainField == null ? "" : explainField.getText();
                if (word == null || word.equals("")) {
                    throw new IllegalStateException("Error: word is empty");
                }
                if (explainFieldText == null || explainFieldText.equals("")) {
                    throw new IllegalStateException("Error: meaning is empty");
                }
                String s = clientSocket.query(new QueryRequest(QueryRequest.TYPE.SAVE, word, explainFieldText));
                showArea.setText("");
                showArea.append(s);
                showArea.append("\n");
            } catch (IllegalStateException |
                    IOException e1) {
                JOptionPane.showMessageDialog(getRootPane(), e1, "Error", JOptionPane.ERROR_MESSAGE);
            }
        }

    }

    public static void main(String[] args) {
        new Client();
    }
}

class ClientSocket {
    private Socket socket;
    private ObjectInputStream inputStream;
    private ObjectOutputStream outputStream;

    ClientSocket() {
        socket = null;
        inputStream = null;
        outputStream = null;
    }

    public void connect(String host, int port) throws UnknownHostException, IOException, IllegalStateException {
        if (socket == null || socket.isClosed()) {
            socket = new Socket(host, port);
            try {
                outputStream = new ObjectOutputStream(socket.getOutputStream());
                inputStream = new ObjectInputStream(socket.getInputStream());
            } catch (IOException e) {
                socket.close();
                throw e;
            }
        } else {
            throw new IllegalStateException("Error: Cannot establish duplicate connection!");
        }
    }

    public String query(QueryRequest queryRequest) throws IllegalStateException, IOException {
        if (socket == null) {
            throw new IllegalStateException("Client is not connected to the server yet, so the request cannot be sent");
        }
        try {
            outputStream.writeObject(queryRequest);
            return (String) inputStream.readObject();
        } catch (ClassNotFoundException e) {
            throw new IllegalStateException("Internal code error!" + e);
        }
    }
}